package com.cn;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @Date :2022/6/2
 * @Author : wjm
 * @Content :设计游戏场景
 */
public class GameScene extends JPanel {
    private final static int WIDTH=800;
    private final static int HEIGHT=400;
    //声明一个图片变量
    BufferedImage image;
    //创建一个小球数组对象
    Ball[] balls=new Ball[50];


    /*
    * 有两种异常
    * 1、编译异常：如;没打，编译异常一定要处理
    * 2、运行异常：运行异常就是系统预判到你运行时候可能出现的错误，比如文件路径找不到
    * 运行异常的两种处理方式：1）向调用者抛出可能出现的异常，最后一级一级往上抛，最后抛给jvm
    *                    2）自己进行捕获处理，对调用者不可见，就是将捕获的异常打印出栈
    *
    * */
    //用构造方法传进来图片
    public GameScene(){
        try {
            //实例化图片对象
            image = ImageIO.read(new File("src/com/cn/background.jfif"));
            //实例化小球的数组对象
            for (int i=0;i<balls.length;i++){
                balls[i]=new Ball();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    //重写父类(Jcomponent)方法,是一个画画的功能
    @Override
    public void paint(Graphics g) {
        //g相当与是画笔，用g画我们传进来的图片
        g.drawImage(image,0,0,null);


        //运行时候窗口左上角文字说明内容
        //设置画笔颜色
        g.setColor(new Color(148, 9, 198, 235));
        //设置字体的格式，大小等,两种写法，第一个是字体格式，第二个是加粗，变斜等，第三个是字体大小
        g.setFont(new Font("楷体",1,18));
        //g.setFont(new Font(Font.SANS_SERIF,Font.BOLD,18));
        //用画笔在画布上写字，注意优先级，先运行的代码在最下面，如果写在画图片代码的上面则看不见写的字
        g.drawString("作者:wjm",30,30);
        g.drawString("项目:小球大作战",30,50);
        g.drawString("小球数目:"+balls.length,30,70);


        /*
        * Java中时间的表示
        *
        * */
        //创建当前系统时间，date默认是英文的且有时区
        Date date=new Date();
        //创建格式化工厂 ,默认的加工格式是22-6-5 下午2:42
        //SimpleDateFormat simpleDateFormat=new SimpleDateFormat();
        //可自定义加工模式,y表示year，yyyy表示四位数，M表示月(mouth)，m表示秒，H表示是24小时制的小时，h表示12小时制
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy/MM/dd/ HH:mm:ss");
        //将获取到的系统时间进行加工
        String time=simpleDateFormat.format(date);
        g.drawString("日期:"+time,30,90);



        //画50个小球
        for (int i=0;i<balls.length;i++){
            //直接调用paintBall（）的方法画小球，同时将画笔g作为参数传递给paintBall（）方法
            balls[i].paintBall(g);
        }

        //重新在画一次画面,就是在action（）方法的定时器中每运行一次ball.move(WIDTH,HEIGHT)就刷新一次页面
        repaint();

    }



    //声明一个固定的时间变量，单位是ms
    int time=10;
    //创建一个游戏运行调度方法
    public void action(){
        //定时器
        Timer timer=new Timer();
        //调用定时任务表
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //让当前数组对象运行
                for (int i=0;i<balls.length;i++){
                    balls[i].move(WIDTH,HEIGHT);
                }
                //用定时任务表调用小球互吃的方法
                eating();
            }
        },time,time);
    }


    //小球互吃的方法
    private void eating() {
        //让每个balls数组对象去判断是否发生碰撞
        //创建一个大球数组，让bigs等于balls这样它们都是指向内存中的同一个数组
        Ball[] bigs=balls;
        //创建一个小球数组，让smalls等于balls这样它们都是指向内存中的同一个数组
        Ball[] smalls=balls;
        //创建一个标记数组，类型为boolean，其数组的默认值为false，当小球被吃就将flase改为ture作为标记
        boolean[] flag=new boolean[smalls.length];
        //声明一个变量来统计每轮有几个小球被标记
        int number=0;
        //遍历大球数组
        for (int i=0;i<bigs.length;i++){
            //判断该大球是否已经被标记，如果被标记则跳过该层for循环
            if (flag[i]==true){ continue; }
            //遍历小球数组
            for (int j=0;j<smalls.length;j++){
                //判断这两个小球是否为同一个小球，是的话就不用比较了之间跳过该层for循环
                if(i==j){continue;}
                //判断该小球是否已经被标记，如果被标记则跳过该层for循环
                if (flag[j]==true){continue; }
                //当大球小球都没被标记，且不是同一个球时，对两个球进行判断是否碰撞,由于isHat是boolean型所以会传回来flase或true值
                boolean a=bigs[i].isHat(smalls[j]);
                if (a==true){
                    //如果小球是被吃掉，则把小球所对应的标记数组的位置的值改为true
                    flag[j]=true;
                    //用来统计被标记了几个球
                    number+=1;
                }
            }
        }

        //创建一个新的数组去存放没有被标记的小球
        Ball[] arr=new Ball[balls.length];
        //声明index变量来作为arr数组索引
        int index=0;
        //遍历新数组，给数组赋值
        for (int i=0;i<arr.length;i++){
            if (flag[i]==false){
                /*arr[i]=smalls[i];
                * 这个语句会出现比如smalls[2]被标记为true，则arr[2]这个位置不会被赋到值
                * 所以可以声明一个变量index来作为arr数组的下标
                */
                arr[index]=smalls[i];
                index++;
            }
        }
        //由于被标记的球不会放进arr中所以arr长度有空余，需要删除多余的长度，可以通过数组的复制实现
        Ball[] arr1= Arrays.copyOf(arr,arr.length-number);
        //再将只有未标记小球，且长度减短的数组arr1赋值给balls
        balls=arr1;
    }

    public static void main(String[] args) {
        //创建画框对象（容器）
        JFrame jframe=new JFrame("java-小球大作战");
        //创建画布对象 (向上造型)
        //JPanel jPanel=new GameScene();
        //创建对象
        GameScene jPanel=new GameScene();
        //将画布添加到画板中
        jframe.add(jPanel);


        //设置画布的size
        jframe.setSize(WIDTH,HEIGHT);
        //设置窗口可见性
        jframe.setVisible(true);
        //设置窗口居中
        jframe.setLocationRelativeTo(null);
        //设置窗口关闭方式（关闭窗口同时结束程序）
        jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置画面优先级（总是显示在最上面）
        jframe.setAlwaysOnTop(true);

        /*
        *调用action（）方法,由于main方法是静态方法，所以只能调用静态方法，
        *而action不是静态所以需要GameScene类的对象来调用
        */
        jPanel.action();
    }
}
